/*
 * UDPTableEntry.java
 * 
 * last update: 16.01.2010 by Stefan Saru
 * 
 * author:	Power
 * 			Alec(panovici@elcom.pub.ro)
 * 
 * Obs:
 */

//#include <defs.h>

package engine;

import java.util.*;
import middle.*;

/**
 * This represents a line from an UDP table.
 */

class UDPTableEntry {


	public static final byte sym_0    = 0;
	public static final byte sym_1    = 1;
	public static final byte sym_x    = 2;
	public static final byte sym_any  = 3;
	public static final byte sym_b    = 4;
	public static final byte sym_f    = 5;
	public static final byte sym_r    = 6;
	public static final byte sym_p    = 7;
	public static final byte sym_n    = 8;
	public static final byte sym_star = 9;
	public static final byte sym_none = 10;

	public static final char [] symChars = {'0', '1', 'X', '?', 'b', 'f',
		'r', 'p', 'n', '*', '-'};

	/**
	 * for combinational entries, only inputs shall be
	 * considered. For sequantial entries, both inputs
	 * and oldInputs will be taken into account when
	 * computing the new state. In this latter case the
	 * contents of oldInputs is considered only if it's !=
	 * fom sym_none (which means that in fact this
	 * particular position into the sequential entry is
	 * a level value).
	 */
	byte [] inputs;
	byte [] oldInputs;
	BitVector output;


	/**
	 * Creates a new UDPTableEntry by parsing the given String
	 * image as taken from the input file and checks
	 * that it has the correct length.
	 * The output value is expected to be the last character
	 * in string.
	 */
	UDPTableEntry(String sImage, int length, byte type) throws ParseException
	{
		inputs = new byte[length-1];
		oldInputs = new byte [length-1];
		boolean isEdgeSensitive = false;
		char[] image = sImage.toCharArray();
		int j = 0, i = 0;
		for (int n = image.length-1; i< n && j < length; i++) {
			switch (image[i]) {
			case '0':
			case '1':
			case 'x':
			case '?':
			case 'b':
				oldInputs[j] = inputs[j] = charToSym(image[i]);
				j++;
				break;
			case 'f':
				inputs[j] = sym_1;
				oldInputs[j]  = sym_0;
				isEdgeSensitive = true;
				j++;
				break;
			case 'r':
				oldInputs[j]  = sym_0;
				inputs[j] = sym_1;
				isEdgeSensitive = true;
				break;
			case 'p':
				inputs[j++] = oldInputs[j]  = sym_p;
				isEdgeSensitive = true;
				break;
			case 'n':
				inputs[j++] = oldInputs[j]  = sym_n;
				isEdgeSensitive = true;
				break;
			case '*':
				oldInputs[j] = inputs[j] = sym_star;
				isEdgeSensitive = true;
				j++;
				break;
			case '(':
				i++;//skip '('
				oldInputs[j] = charToSym(image[i++]);
				inputs[j++] = charToSym(image[i++]); //skip ')'
				if (oldInputs[j-1] == inputs[j-1] && inputs[j-1] == sym_any)
					oldInputs[j-1] = inputs[j-1] = sym_star;
				isEdgeSensitive = true;
			}
		}

		if(j !=  length -1) 
			throw new ParseException(": error: bad table entry: too " +
					(j < length ? "few" : "many") + " entries");

		if (type == UDPDescription.COMType || !isEdgeSensitive)
			oldInputs = null; //don't laugh ! yhis approach simplifies much of
		// the input tables filling code from above.

		switch(image[i]){
		case '1' :
			output = BitVector.b1();
			break;
		case '0':
			output = BitVector.b0();
			break;
		case 'x':
			output = BitVector.bX();
			break;
			//by default output is "-" : ouput will be null
		}

		xConsole.debug("new " +
				(oldInputs == null ? "combinational" : "sequential") +
				" UDPTableEntry : " + this);
	}

	byte charToSym (char c) {
		switch (c) {
		case '0' : return sym_0;
		case '1' : return sym_1;
		case 'b' : return sym_b;
		case '?' : return sym_any;
		case 'x' : return sym_x;
		}
		throw new Error("incorrect argument: \'" + c + "\'");
	}

	boolean isEdgeEntry () {
		return oldInputs != null;
	}

	/**
	 * Verifies that  matches all the conditions
	 * in this entry
	 */
	boolean match (Result last, Result current) {
		BitVector l = last.getBits(), c = current.getBits();
		xConsole.debug("UDPTableEntry (" + this + ") : matching " + l +
				" - -> " + c);
		if (oldInputs == null)
			return matchLevel(c);
		return matchEdge(l, c);
	}

	boolean matchEdge(BitVector l, BitVector c) {
		int length = l.n;
		for (int i = inputs.length -1, j = 0; j < length; j++, i--) {
			byte lv = (byte) l.getBit(j), cv = (byte) c.getBit(j);
			switch (inputs[i]) {
			case sym_p:
				if (lv == 0) {
					if (cv != 1 && cv != BitVector.X) return false; //(01), (0X)
				} else {
					if (lv == BitVector.X) {
						if (cv != 1) return false; //(X1)
					} else
						return false;
				}
				break;
			case sym_n:
				if (lv == 1) {
					if (cv != 0 && cv != BitVector.X) return false;  //(10), (1X)
				} else {
					if (lv == BitVector.X) {
						if (cv != 0) return false; //(X0)
					} else
						return false;
				}
				break;
			case sym_star:
				if (lv == cv) return false;
				break;
			default:
				if (!matchLevelSymbol(oldInputs[i], lv) ||
						!matchLevelSymbol(inputs[i], cv)) return false;
			}
		}
		return true; //all ok
	}

	boolean matchLevelSymbol(byte symbol, int v) {
		xConsole.debug("UDPTableEntry.matchLevelSymbol : sym: " + symChars[symbol] +
				" v: " + v);
		switch (symbol) {
		case sym_0:
			if (v != 0) return false;
			return true;
		case sym_1:
			if (v != 1) return false;
			return true;
		case sym_b:
			if (v != 1 && v != 0) return false;
			return true;
		case sym_x:
			if (v != BitVector.X) return false;
			return true;
		case sym_any:
			return true;
		default:
			throw new Error("unknown level sym: " + symbol);
		}
	}

	boolean matchLevel (BitVector b) {
		xConsole.debug("UDPTableEntry.matchLevel for " + b + " n: " + b.n +
				" msb: " + b.msb + " lsb: " + b.lsb + " inc: " + b.increment);
		for (int i = 0, j = b.msb;; j-=b.increment, i++) {
			if (!matchLevelSymbol(inputs[i], b.getBit(j))) return false;
			if (j == b.lsb) break;
		}
		return true;
	}

	public String toString() {
		String s = "";
		for (int i = 0; i < inputs.length;  i++) {
			if (i == inputs.length - 1 && oldInputs != null)
				s += " : ";
			if (oldInputs != null)
				if (inputs[i] != oldInputs[i]) {
					s += " (" + symChars[oldInputs[i]] + "," + symChars[inputs[i]] + ")";
					continue;
				}
			s += " " + symChars[inputs[i]];
		}

		s += " : " + output;
		return s;
	}
}



